#lang racket

;; The pairElmination strategy for sudoku
;; If two cells in a row (column, or box) contain the same two candidates as their only two possibilities,
;; eliminate the two candidates from all other cells in the same row (column or box)
;; By: Theresa Wellington

(provide pairElimination pairEliminationRows)

(define pairElimination
  (lambda (possibilityMatrix)
    (pairEliminationRows possibilityMatrix 0)
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pairElimination Helper Methods ;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; check number of possibleValues for a cell
(define possibleValueCount
  (lambda (cell pos count)
    ;; if at the end of the cell, return count
    (if (equal? pos 9) count
        ;; else if the cell pos is #t, call possibeValueCount with increment count
        (and (if (vector-ref cell pos) (possibleValueCount cell (+ pos 1) (+ count 1))
                 ;; else call possibleValueCount again without incrementing count
                 (and (possibleValueCount cell (+ pos 1) count)))))))

;; find values to remove
(define findValue
  (lambda (possibleValues pos)
    (if (vector-ref possibleValues pos) pos
        (and (findValue possibleValues (+ pos 1))))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pairEliminationRows ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; If two cells in a row have two candidates a and b, a and b can be removed from all other cells in the row.
;; call pairEliminationSingleRow for each row)
(define pairEliminationRows
  (lambda (possibilityMatrix startRow)
    (if (equal? startRow 8) (pairEliminationSingleRow (vector-ref possibilityMatrix startRow) 0)
        (and (pairEliminationSingleRow (vector-ref possibilityMatrix startRow) 0) (pairEliminationRows possibilityMatrix (+ startRow 1))))))
               

;; for each column in row, set possibile values and check against every other column in row
(define pairEliminationSingleRow
  (lambda (row startCol)
    ;; nothing to compare last column with
    (cond ((not (equal? startCol 8))
        (let ((possibleValues (vector-ref row startCol)))
          ;; if there are two possible values for the current cell, look for a match
          (cond ((equal? 2 (possibleValueCount possibleValues 0 0))
            (lookForMatch row possibleValues startCol (+ startCol 1)) )))
        ;; check the rest of the columns in the row
        (pairEliminationSingleRow row (+ startCol 1))))))

(define lookForMatch
  (lambda (row possibleValues firstCol otherCol)
    ;; if not at the end of the vector
    (cond ((not (equal? otherCol 9))
        ;; if possibility vectors are equal, remove values from row except in 2 columns
        (if (equal? possibleValues (vector-ref row otherCol))
            (removeRowValuesFromPairElim row possibleValues firstCol otherCol)
            ;; else keep checking
            (and (lookForMatch row possibleValues firstCol (+ otherCol 1))))))))

(define removeRowValuesFromPairElim
  (lambda (row possibleValues firstCol otherCol)
    ;; find two values to remove
    (let ((firstRemoveValue (findValue possibleValues 0)))
      (let ((secondRemoveValue (findValue possibleValues (+ firstRemoveValue 1))))
        ;; remove them
        (removeRowValue row firstRemoveValue firstCol otherCol 0)
        (removeRowValue row secondRemoveValue firstCol otherCol 0)))))

(define removeRowValue
  (lambda (row removeValue skipCol skipCol2 startCol)
     (cond ((not (equal? startCol 9))
         ;; if startCol equal to skipCol, do next recursive call
         (if (equal? startCol skipCol) (removeRowValue row removeValue skipCol skipCol2 (+ startCol 1))
             ;; if startCol equal to skipCol2, do next recursive call
             (and (if (equal? startCol skipCol2) (removeRowValue row removeValue skipCol skipCol2 (+ startCol 1))
                  ;; else remove value from possibility vector
                  (and (vector-set! (vector-ref row startCol) removeValue #f)
                       (removeRowValue row removeValue skipCol skipCol2 (+ startCol 1))))))))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pairEliminationCols ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: implement pair elimination for columns

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pairEliminationBoxes ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: implement pair elimination for boxes
